/*
 * Planeta.cpp
 *
 *  Created on: 09/01/2010
 *      Author: San Martín, Matías I.
 */

#include "Planeta.h"

#include <sstream>
#include <cmath>

#include <sys/timeb.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>


//datos de mensajes con los que se trabaja
#include "../../Common/Mensajes/DatosCambiarRumboAvisoDestino.h"
#include "../../Common/Mensajes/DatosCambiarRumboOrden.h"
#include "../../Common/Mensajes/DatosCambiaDuenio.h"
#include "../../Common/Mensajes/DatosCrearFlotaAvisoDestino.h"
#include "../../Common/Mensajes/DatosCrearFlotaOrden.h"
#include "../../Common/Mensajes/DatosMensajeFactory.h"

#include "../../../Framework/Excepciones/InicializacionExcepcion.h"

#include "../../../Middleware/Common/IdFlota.h"
#include "../../../Aplicacion/Common/Registry.h"

using namespace framework::io;

using namespace std;

namespace aplicacion {
namespace planeta {

const string Planeta::PATH_FIFO = "./";
const string Planeta::EXT_FIFO = "fifo";

Planeta::Planeta(IdPlaneta identificador, T_TAMANIO_PLANETA tamanio,
Posicion posicion) :
	_tahora(0),
	_receptor(NULL), _emisor(NULL),
	_pipeRecepcion(-1)	{
	_info._identificador = identificador;
	_info._tamanio = tamanio;
	_info._posicion = posicion;
	_info._cantidadNaves = MULTIPLICADOR_NAVES * tamanio;
}

Planeta::~Planeta() {
}


void Planeta::init (MsjReceptor* receptor, MsjEmisor* emisor) {

	if (!receptor)
		throw new InicializacionExcepcion("Receptor de mensajes no puede ser NULL");

	if (!emisor)
			throw new InicializacionExcepcion("Emisor de mensajes no puede ser NULL");

	//genero el identificador como Planeta <id planeta>
	std::stringstream ss;
	ss << "Planeta " << _info._identificador.getId();

	logger.set_pname(ss.str());

	_receptor = receptor;
	_emisor = emisor;

	const char* nombrePipe = this->getFdName().c_str();

	//creo el pipe de recepción de mensajes
	if (mkfifo(nombrePipe,0666) == -1 && errno != EEXIST)
	{
		stringstream msj;
		msj << "No se pudo crear el pipe de mensajes: " << strerror(errno);
		throw new InicializacionExcepcion(msj.str());
	}

	_cambiaronFlotas = false;

	//registro el pipe en el receptor
	/* Es necesario registrar antes de abrir por lectura porque la apertura
	 * para solo lectura es bloqueante por defecto */
	_receptor->registrarPipe(nombrePipe,_info._identificador);

	logger << "Esperando por apertura de pipe." << logger.endl;
	_pipeRecepcion = open(nombrePipe,O_RDONLY);

	//abro el pipe para lectura
	if (_pipeRecepcion == -1)
	{
		stringstream msj;
		msj << "No se pudo abrir el pipe de mensajes para lectura: " << strerror(errno);
		throw new InicializacionExcepcion(msj.str());
	}
	logger << "Inicializado" << logger.endl;
}


/**
 * Devuelve la diferencia en ms del tiempo actual con la referencia
 */
T_TIEMPO getMillisPasaron(const struct timeb& treferencia)
{
	struct timeb tactual;
	ftime( &tactual );
	return (tactual.time - treferencia.time) * 1000 +
			tactual.millitm - treferencia.millitm;
}

std::string Planeta::getFdName()
{
	std::stringstream stm;
	stm << PATH_FIFO << _info._identificador.getUid() << "." << EXT_FIFO;
	return stm.str();
}

void Planeta::stop()
{
	_ejecutar = false;
}

bool Planeta::recibirMsjStart()
{
	//fd por los cuales se espera entrada (en este caso el de mensajes)
	fd_set fdset;
	//mensaje que se recibira
	MensajeAplicacion msj;

	logger << LogType::LOG_DEBUG_1 << "Esperando mensaje de START" << logger.endl;
	bool faltaRecibir = true;

	do {
		FD_ZERO(&fdset);
		FD_SET(_pipeRecepcion, &fdset);

		//Espero a que llegue algo al pipe de mensajes
		int retSelect = select(_pipeRecepcion + 1,&fdset,NULL,NULL,NULL);

		//si se recibieron datos
		if (retSelect > 0)
		{
			//recibo el mensaje
			this->recibirMensaje(msj);

			//comprobacion de robustez, verifico que el destino sea el planeta
			IdPlaneta destino;
			msj.getEncabezado().getDestino(destino);
			if (destino == _info._identificador &&
					//se espera por el inicio del juego, sino se ignora el msj
					msj.getDatos().getTipo() == INICIO_JUEGO )
			{
				//se guarda el id del juego al que se pertenece
				_info._duenio.setContainerId(
						static_cast<DatosInicioJuego&>(msj.getDatos())._idJuego);

				//se recibio el mensaje esperado
				faltaRecibir = false;
			} //verificacion de destino y tipo de mensaje validos
		} //verificacion de datos recibidos

	//se termina una vez que se recibio el mensaje o si se aborto la ejecucion
	}while(faltaRecibir && _ejecutar);

	if (faltaRecibir)
		logger << LogType::LOG_ERROR <<
		LogColor::RED << "Fallo la recepcion del mensaje de start" << logger.endl;
	else
		logger << LogType::LOG_DEBUG_1 << "Recibido el mensaje de start" << logger.endl;

	return !faltaRecibir;
}

void Planeta::run() {

	_ejecutar = true;

	//espero el mensaje de inicio de juego para iniciar la ejecucion
	if (recibirMsjStart())
	{

		_tahora = 0;
		struct timeb tultimaActualizacion;
		ftime( &tultimaActualizacion );

		//cuantos ms se tarda en generar las naves necesarias para avisar
		const T_TIEMPO duracionGenerarNumAviso = (CANT_NAVES_GENERADAS_AVISO * 1000 /
				(_info._tamanio * MULTIPLICADOR_NAVES_SEG))
				+ 0.5 /*redondeo*/;

		//mantiene el tiempo faltante para que arriben las naves necesarias para avisar
		//en un principio no hay flotas que arriben
		T_TIEMPO tiempoAvisoArribos = TIEMPO_MAX;

		//mantiene el tiempo faltante para que se generen las naves necesarias para avisar
		//al principio, como no tiene dueño el planeta, no se generan naves
		T_TIEMPO tiempoAvisoGeneradas = TIEMPO_MAX;


		//fd por los cuales se espera entrada (en este caso el de mensajes)
		fd_set fdset;

		//tiempo de espera por mensaje hasta timeout
		timeval tiempoTimeout;

		logger << LogColor::BLUE << "Inicia ejecucion con " << _info._cantidadNaves << " naves" << logger.endl;

		while (_ejecutar)
		{
			/* el tiempo hasta el proximo aviso es el menor de los tiempos
			 * entre el arribo de las naves necesarias y la generacion de las
			 * naves necesarias */
			T_TIEMPO tiempoProximoAviso = (tiempoAvisoArribos < tiempoAvisoGeneradas)?
					tiempoAvisoArribos:tiempoAvisoGeneradas;

			//asigno el fd del pipe (hay que hacerlo cada vez pq se modifica en la llamada)
			FD_ZERO(&fdset);
			FD_SET(_pipeRecepcion, &fdset);

			tiempoTimeout.tv_sec = tiempoProximoAviso / 1000;
			tiempoTimeout.tv_usec = (tiempoProximoAviso % 1000) * 1000;

			//Espero a que llegue algo al pipe de mensajes o que se de timeout
			int retSelect = select(_pipeRecepcion + 1,&fdset,NULL,NULL,&tiempoTimeout);

			logger << LogType::LOG_DEBUG_1 << "_______________________________________" << logger.endl;
			logger << LogType::LOG_DEBUG_1 << "Termina espera sobre fd" << logger.endl;

			if (retSelect != -1)
			{
				//obtengo la cantidad de tiempo que paso desde la ultima actualizacion
				T_TIEMPO tiempoPaso = getMillisPasaron(tultimaActualizacion);

				//se actualiza el _tahora para reflejar el tiempo pasado
				_tahora += tiempoPaso;

				logger << LogType::LOG_DEBUG_1 << "Tiempo paso: " << tiempoPaso << logger.endl;

				//si paso el tiempo se actualiza el estado del planeta
				if (tiempoPaso > 0)
				{

					//actualizo los tiempos para aviso
					tiempoAvisoGeneradas -= (tiempoPaso >= tiempoAvisoGeneradas)? tiempoAvisoGeneradas:tiempoPaso;

					//solo se actualiza el tiempo de arribos si hay naves por arribar
					if (_flotas.size())
						tiempoAvisoArribos -= (tiempoPaso >= tiempoAvisoArribos)? tiempoAvisoArribos:tiempoPaso;

					//si el planeta tiene dueño
					if (_info._duenio.isDefined())
					{
						//genero las naves por el paso del tiempo
						this->generarNaves(tiempoPaso);
					}

					if (tiempoAvisoGeneradas == 0)
					{
						//se envia el mensaje de notificacion de la generacion
						this->notificarCantidadNaves();

						//reinicio el tiempo para el aviso de naves generadas
						tiempoAvisoGeneradas = duracionGenerarNumAviso;
					}

					//actualizo el estado de las flotas (arriban naves)
					//se indica si es necesario notificar el arribo
					this->arribarNaves(_tahora,tiempoAvisoArribos == 0);
				}

				//si se salió porque hay mensajes en el pipe
				if (retSelect > 0)
				{
					logger << LogType::LOG_DEBUG_1 << "¡¡Llego mensaje!!" << logger.endl;
					MensajeAplicacion mensaje;

					IdJugador duenioAnterior = _info._duenio;

					//recibo el mensaje
					this->recibirMensaje(mensaje);

					//proceso el mensaje
					this->procesarMensaje(mensaje);

					//si el dueño cambio, se reinicia el tiempo de generacion
					if (duenioAnterior != _info._duenio)
					{
						tiempoAvisoGeneradas = duracionGenerarNumAviso;
					}
				}

				//si se actualizo el estado
				if (tiempoPaso > 0)
				{
					/* actualizo el tiempo de arribos en caso de que se haya enviado
					 * la notificacion o se hayan agregado flotas */
					if (tiempoAvisoArribos == 0 || _cambiaronFlotas)
					{
						tiempoAvisoArribos = _flotas.getTiempoArribaran(CANT_NAVES_ARRIBO_AVISO);
						logger << LogType::LOG_DEBUG_1 << "Tiempo aviso arribos: " << tiempoAvisoArribos << logger.endl;
						_cambiaronFlotas = false;
					}

					//se actualiza el tiempo de la ultima actualizacion
					/* (se espera a ultimo momento para actualizar el tiempo porque
					 * se quiere tomar el ultimo instante antes de bloquearse nuevamente
					 * -es como si el procesamiento fuera instantaneo- ) */
					ftime( &tultimaActualizacion );
				}
			}else{
				logger << LogType::LOG_ERROR <<
					LogColor::RED << "ERROR: fallo la espera por datos: " << logger.err << logger.endl;
			}
		} //ciclo de ejecucion

		logger << LogColor::BLUE << "Termina ejecucion" << logger.endl;
	} //recepcion de mensaje de start
}

void Planeta::notificarCantidadNaves()
{
	MensajeAplicacion msj;

	//creo los datos del mensaje
	DatosCantidadNaves* datosMsj =
			DatosMensajeFactorySingleton::instance().createCantidadNaves();

	//cargo la informacion
	datosMsj->_cantidadNaves = this->_info._cantidadNaves;

	//asigno los datos al mensaje
	msj.setDatos(datosMsj);

	//envio el mensaje al juego
	this->enviarAJuego(msj);
}

void Planeta::notificarArribaronNaves(const IdFlota& flotaArribo, size_t cantidadNavesFlota)
{
	MensajeAplicacion msj;

	//creo los datos del mensaje
	DatosArriboNave* datosMsj =
			DatosMensajeFactorySingleton::instance().createArriboNave();

	//cargo la informacion
	datosMsj->_idFlota = flotaArribo;
	datosMsj->_cantidadNavesFlota = cantidadNavesFlota;

	//asigno los datos al mensaje
	msj.setDatos(datosMsj);

	//envio el mensaje al juego
	this->enviarAJuego(msj);
}

/**
 * Envía un mensaje de notificación de cambio de dueño
 */
void Planeta::notificarCambioDuenio(const IdJugador& duenioAnterior)
{
	MensajeAplicacion msj;
	//creo los datos del mensaje
	DatosCambiaDuenio* datosMsj =
			DatosMensajeFactorySingleton::instance().createCambiaDuenio();

	//cargo el dueño anterior a la notificación
	datosMsj->_duenioAnterior = duenioAnterior;

	//cargo el dueño nuevo a la notificación
	datosMsj->_duenioNuevo = _info._duenio;

	//asigno los datos al mensaje
	msj.setDatos(datosMsj);

	//envío la notificación
	if (this->enviarAJuego(msj) == -1)
	{
		//TODO VER QUE HACER. EXCEPCION??
	}
}

void Planeta::cambiarDuenio(const IdJugador& nuevoDuenio)
{
	IdJugador duenioAnterior = _info._duenio;
	_info._duenio = nuevoDuenio;

	this->notificarCambioDuenio(duenioAnterior);
}

/**
 * Genera arribo de las naves debido al paso del tiempo indicado
 */
void Planeta::arribarNaves(T_TIEMPO tiempo, bool notificar)
{
	size_t cantidadArriban = _flotas.getCantidadArribaran(tiempo);

	//si arriba alguna nave, genero el arribo
	if (cantidadArriban > 0)
	{
		//hago arribar a las naves y verifico quien quedo en el planeta
		t_parIdJugadorCantidad queda;
		queda.first = _info._duenio;
		queda.second = _info._cantidadNaves;

		logger << LogType::LOG_DEBUG_1 << "Esta antes de arribar: "
				<< LogColor::BROWN << queda.first << LogColor::COLOR_FIN
				<<"(" << queda.second << ")" << logger.endl;

		queda = _flotas.getQuedoUltimo(tiempo,queda);

		logger << LogType::LOG_DEBUG_1 << "Esta despues de arribar: " <<
				LogColor::BROWN << queda.first << LogColor::COLOR_FIN
				<< "(" << queda.second << ")" << logger.endl;

		//de ser necesario se cambia de dueño
		if (queda.first != _info._duenio)
			this->cambiarDuenio(queda.first);

		//actualizo cantidad de naves que quedan
		_info._cantidadNaves = queda.second;

		//notifico las naves que arribaron (segun corresponda)
		t_idFlotasCantidadCollection* arribaron =
									_flotas.getCantidadNavesFlotasArribaron();

		/* recorro la lista de flotas que arribaron alguna nave para, enviando
		 * notificacion de arribos para las que correspondan */
		t_idFlotasCantidadCollection::iterator fin = arribaron->end();
		for (t_idFlotasCantidadCollection::iterator i = arribaron->begin();
				i != fin;
				i++)
		{
			logger << "Flota arribo: " <<
			LogColor::BLUE << i->first << LogColor::COLOR_FIN
			<< "(" <<
			LogColor::BLUE << i->second  << LogColor::COLOR_FIN
			<< ")" << logger.endl;

			//si arribo la flota o se fuerza la notificacion, se notifica
			if (i->second == 0 || notificar)
			{
				this->notificarArribaronNaves(i->first,i->second);

				//elimino la informacion de la flota
				i = arribaron->erase(i);
			}
		}
	}
}//arribarNaves

/**
 * Genera las naves debido al paso del tiempo indicado
 */
void Planeta::generarNaves(T_TIEMPO tiempoPaso)
{
	size_t cantGeneradas = (tiempoPaso * _info._tamanio * MULTIPLICADOR_NAVES_SEG / (double)1000) + 0.5;
	_info._cantidadNaves += cantGeneradas;
	logger << "Genero naves: " <<
			LogColor::BLUE << cantGeneradas << LogColor::COLOR_FIN
			<< " naves total: " <<
			LogColor::BLUE << _info._cantidadNaves << logger.endl;
}

/**
 * Recibe un mensaje desde el pipe y carga los datos en el mensaje indicado
 */
void Planeta::recibirMensaje(MensajeAplicacion& mensaje)
{
	mensaje.load(_pipeRecepcion);
}

void Planeta::ejecutarCreacionFlota(MensajeAplicacion& mensaje)
{
	DatosCrearFlotaAvisoDestino& datos =
					dynamic_cast<DatosCrearFlotaAvisoDestino&>(mensaje.getDatos());

	//obtengo la información del planeta origen (necesito saber la posición de partida)
	InformacionPlaneta infoOrigen;
	mensaje.getEncabezado().getOrigen(infoOrigen._identificador);

	if (!RegistrySingleton::instance().getDirectorio().getInformacionPlaneta(&infoOrigen))
	{
		//TODO ERROR, NO SE PUDO OBTENER LA INFO DEL PLANETA
	}

	//creo la nueva flota a agregar a partir de la información del mensaje
	Flota* flotaNueva = new Flota(
				datos._idFlota,
				datos._cantidadNaves,
				datos._semilla,
				infoOrigen._tamanio * FACTOR_ESCALA_TAMANIO,
				infoOrigen._posicion, _info._posicion,
				_tahora	);

	logger << "Msj -> Creada flota " <<
			LogColor::BLUE << flotaNueva->getId() << LogColor::COLOR_FIN
			<< " con " <<
			LogColor::BLUE << flotaNueva->getCantidadNaves() << LogColor::COLOR_FIN <<
			" naves" << logger.endl;

	//agrego la flota a la lista
	_flotas.add(flotaNueva);

	//se indica que se agregaron nuevas flotas
	_cambiaronFlotas = true;
}//ejecutarCreacionFlota

/**
 * Avisa al destino que cree la flota
 * Avisa a los jugadores de la creación
 */
void Planeta::ejecutarAvisarCrearFlota(MensajeAplicacion& mensaje)
{
	DatosCrearFlotaOrden& datosOrden =
						dynamic_cast<DatosCrearFlotaOrden&>(mensaje.getDatos());

	//verificacion de robustez. Solo el dueño puede crear una flota en el planeta
	IdJugador origen;
	mensaje.getEncabezado().getOrigen(origen);
	//si el origen del mensaje no es el dueño
	if (origen != _info._duenio)
	{
		//se loguea un error y se ignora la orden
		logger << LogType::LOG_ERROR << LogColor::RED <<
				"Msj Aviso Crear Flota -> Se recibió una"
				" orden de crear flota del jugador " << origen << " que no es el"
				" dueño actual del planeta (" << _info._duenio << ") se ignora"
				" la orden"	<< logger.endl;
		return;
	}

	if (datosOrden._proporcionNaves == 0.0)
	{
		logger << LogType::LOG_WARNING << "Msj Aviso Crear Flota -> se ignora el mensaje ya que"
				" la flota no contendria naves (proporcion indicada es 0)"
				<< logger.endl;
		return;
	}

	logger << "Msj -> Aviso Crear Flota -> cantidad naves: "
					<< LogColor::BLUE << _info._cantidadNaves << logger.endl;

	/* validación del contenido (tomo el valor absoluto y lo saturo a 1.0 si
	 * se indicó un tamaño mayor) */
	datosOrden._proporcionNaves
			= (fabs(datosOrden._proporcionNaves) <= 1.0) ?
					fabs(datosOrden._proporcionNaves) : 1.0;

	//calculo la cantidad de naves que tendra la flota
	size_t cantidadNaves = _info._cantidadNaves * datosOrden._proporcionNaves;

	/* verifico que la cantidad de naves indicadas sea menor al maximo,
	 * si no lo es, trunco a la cantidad maxima */
	if (cantidadNaves > MAX_NAVES_FLOTAS)
	{
		logger << LogType::LOG_WARNING << "Msj Aviso Crear Flota -> "
				"Con la proporcion indicada (" << datosOrden._proporcionNaves <<
				") se crearia una flota con " <<
				LogColor::BLUE << cantidadNaves
				<< LogColor::COLOR_FIN << " naves."
				" Esta cantidad es mayor al maximo permitido (" <<
				LogColor::BLUE << MAX_NAVES_FLOTAS << LogColor::COLOR_FIN
				<< "). Se crea una flota con la cantidad de naves maxima, esto "
				"representa una proporcion de " << ((double)MAX_NAVES_FLOTAS)/_info._cantidadNaves
				<< " respecto del total en el planeta." << logger.endl;
		cantidadNaves = MAX_NAVES_FLOTAS;
	}

	//decremento esa cantidad de la que hay en el planeta
	_info._cantidadNaves -= cantidadNaves;

	//TODO FALTARIA VALIDACION DE QUE EL DESTINO EXISTA

	MensajeAplicacion msj;

	IdJugador idJugadorFlota;
	mensaje.getEncabezado().getOrigen(idJugadorFlota);

	//------------------- envío el aviso al juego -----------------
	//creo los datos del mensaje de aviso que se
	DatosCrearFlotaAvisoJugador* datosMsjAvisoJugador =
			DatosMensajeFactorySingleton::instance().createCrearFlotaAvisoJugador();

	//cargo la informacion
	datosMsjAvisoJugador->_idFlota.setId(datosOrden._idFlota);
	datosMsjAvisoJugador->_idFlota.setIdJugador(idJugadorFlota);
	datosMsjAvisoJugador->_cantidadNaves = cantidadNaves;
	datosMsjAvisoJugador->_idDestino = datosOrden._planetaDestino;
	datosMsjAvisoJugador->_semilla = datosOrden._semilla;

	//asigno los datos al mensaje
	msj.setDatos(datosMsjAvisoJugador);

	//envío el mensaje al juego
	this->enviarAJuego(msj);

	//----------- envío el aviso al planeta destino ------
	//creo los datos del mensaje de aviso que se
	DatosCrearFlotaAvisoDestino* datosMsjAvisoDestino =
			DatosMensajeFactorySingleton::instance().createCrearFlotaAvisoDestino();

	//cargo la informacion
	//id de flota
	datosMsjAvisoDestino->_idFlota.setId(datosOrden._idFlota);
	datosMsjAvisoDestino->_idFlota.setIdJugador(idJugadorFlota);

	//cantidad de naves
	datosMsjAvisoDestino->_cantidadNaves = cantidadNaves;

	//semilla
	datosMsjAvisoDestino->_semilla = datosOrden._semilla;


	//asigno los datos al mensaje
	msj.setDatos(datosMsjAvisoDestino);

	//envío el mensaje al destino
	this->enviarAPlaneta(datosOrden._planetaDestino,msj);
}//ejecutarAvisarCrearFlota

void Planeta::ejecutarAvisarCambiarRumboFlota(MensajeAplicacion& mensaje)
{
	DatosCambiarRumboOrden& datos =
				dynamic_cast<DatosCambiarRumboOrden&>(mensaje.getDatos());

	//verificación de robustez (pertenencia de la flota al planeta)
	IdFlota idFlotaCambiarRumbo;
	idFlotaCambiarRumbo.loadFromMensaje(datos._idFlota,mensaje.getEncabezado().getOrigen());


	ssize_t posFlota = _flotas.getIndex(idFlotaCambiarRumbo);
	if (posFlota > -1)
	{
		Flota& flotaCambiarDestino = _flotas.at((size_t)posFlota);

		//verificacion de robustez. Solo el dueño puede cambiar el rumbo de la flota
		IdJugador origen;
		mensaje.getEncabezado().getOrigen(origen);
		//si el origen del mensaje no es el dueño
		if (origen != flotaCambiarDestino.getDuenio())
		{
			//se loguea un error y se ignora la orden
			logger << LogType::LOG_ERROR << LogColor::RED <<
					"Msj Aviso Cambiar Rumbo Flota -> "
					"Se recibió una orden de cambiar el rumbo de la flota del jugador "
					<< origen << " que no es el dueño  (" <<
					flotaCambiarDestino.getDuenio() << ") se ignora"
					" la orden"	<< logger.endl;
			return;
		}

		logger << LogType::LOG_DEBUG_1 << flotaCambiarDestino.getId() <<
				" - flotaCambiarDestino.getTamanio()==" <<
				flotaCambiarDestino.getTamanio() << logger.endl;

		//----------- envío el aviso al planeta (nuevo) destino ------

		MensajeAplicacion msj;
		//creo los datos del mensaje de aviso que se
		DatosCambiarRumboAvisoDestino* datosMsjAvisoDestinoCambiarRumbo =
				DatosMensajeFactorySingleton::instance().createCambiarRumboAvisoDestino(&flotaCambiarDestino);

		//cargo la informacion
		//cargo el id de la flota
		datosMsjAvisoDestinoCambiarRumbo->_idFlota = flotaCambiarDestino.getId();

		//asigno los datos al mensaje
		msj.setDatos(datosMsjAvisoDestinoCambiarRumbo);

		//envío el mensaje al destino
		this->enviarAPlaneta(datos._idDestinoNuevo,msj);

		//------------------- envío el aviso al juego -----------------
		//creo los datos del mensaje de aviso que se
		DatosCambiarRumboAvisoJugador* datosMsjAvisoJugadorCambiarRumbo =
				DatosMensajeFactorySingleton::instance().createCambiarRumboAvisoJugador(&flotaCambiarDestino);

		//cargo la informacion
		//cargo el id de la flota
		datosMsjAvisoJugadorCambiarRumbo->_idFlota = flotaCambiarDestino.getId();

		//cargo el id del nuevo destino
		datosMsjAvisoJugadorCambiarRumbo->_idNuevoDestino = datos._idDestinoNuevo;

		//asigno los datos al mensaje (eliminando los asignados previamente)
		msj.setDatos(datosMsjAvisoJugadorCambiarRumbo);

		//envío el mensaje al juego
		this->enviarAJuego(msj);

		/* elimino la flota de las asociadas al planeta (ya que esta no esta mas
		 * asociada al mismo */
		_flotas.erase(posFlota);
		_cambiaronFlotas = true;
	}
	else
	{
		logger << LogType::LOG_ERROR << LogColor::RED <<
				"ERROR: Se intento cambiar de rumbo a una flota que no esta "
				"asociada al planeta, id de flota: " << idFlotaCambiarRumbo << logger.endl;
	}
}

void Planeta::ejecutarCambiarRumboFlota(MensajeAplicacion& mensaje)
{
	DatosCambiarRumboAvisoDestino& datos =
			dynamic_cast<DatosCambiarRumboAvisoDestino&>(mensaje.getDatos());

	//obtengo la flota guardada en el mensaje (estado de la flota)
	Flota* flotaNueva = dynamic_cast<Flota*>(datos._estadoFlota);

	//cargo el identificador
	flotaNueva->setId(datos._idFlota);
	//asigno el tiempo para que los arribos se generen desde el tiempo actual
	flotaNueva->cambiarTiempo(_tahora);

	//agrego la flota a la lista
	_flotas.add(flotaNueva);
	_cambiaronFlotas = true;
}

void Planeta::ejecutarCambioDueno(MensajeAplicacion& mensaje)
{
	//solo es para el caso en que se asigna el planeta
	if (!_info._duenio.isDefined())
	{
		this->cambiarDuenio(dynamic_cast<DatosCambiaDuenio&>(mensaje.getDatos())._duenioNuevo);
	}else{
		logger << LogType::LOG_ERROR <<
				LogColor::RED << "Se recibio mensaje de cambio de dueño "
				"cuando el dueño ya esta inicializado" << logger.endl;
	}
}

void Planeta::ejecutarAbandonaJugador(MensajeAplicacion& mensaje)
{
	IdJugador origen;
	mensaje.getEncabezado().getOrigen(origen);

	//verificacion de robustez, el jugador que abandona tiene que ser el actual dueño
	if (_info._duenio == origen)
	{
		IdJugador sinDuenio;
		sinDuenio.setContainerId(origen.getJuego());
		//si abandono el dueño, el nuevo dueño es el jugador neutral
		this->cambiarDuenio(sinDuenio);
	}else{
		/* si se avisa que abandono un jugador que no es el dueño, se eliminan
		 * todas las flotas que pudieran haber del jugador que abandono */
		_flotas.removeByDuenio(origen);

		logger << "Las flotas del jugador " << origen <<
					" fueron " << LogColor::BLUE <<
					"eliminadas" <<
					LogColor::COLOR_FIN <<
					" debido a que dicho jugador abandono " <<
					" el juego." << logger.endl;
	}

}

/**
 * Actualiza la fuente (asignando el id del planeta) y envía el mensaje
 * al juego asociado al dueño
 */
int Planeta::enviarAJuego(MensajeAplicacion& mensaje)
{
	//cargo el origen (identificador)
	mensaje.getEncabezado().setOrigen(_info._identificador);

	//cargo el destino
	mensaje.getEncabezado().setDestino(_info._duenio.getJuego());

	//cargo el tipo
	mensaje.getEncabezado().setTipo(mensaje.getDatos().getTipo());
	//envío el mensaje
	return _emisor->enviar(_info._duenio.getJuego(),mensaje);
}

/**
 * Actualiza la fuente (asignando el id del planeta) y el destino (según el
 * indicado) y envía el mensaje destino
 */
int Planeta::enviarAPlaneta(IdPlaneta destino, MensajeAplicacion& mensaje)
{

	//cargo el origen (identificador)
	mensaje.getEncabezado().setOrigen(_info._identificador);
	//cargo el destino
	mensaje.getEncabezado().setDestino(destino);

	//cargo el tipo
	mensaje.getEncabezado().setTipo(mensaje.getDatos().getTipo());

	//envío el mensaje
	return _emisor->enviar(destino,mensaje);
}

void Planeta::procesarMensaje(MensajeAplicacion& mensaje)
{

	logger << LogType::LOG_DEBUG_1 << "Proceso mensaje --> "
						<< mensaje << logger.endl;

	IdPlaneta destinoMensaje;
	mensaje.getEncabezado().getDestino(destinoMensaje);
	if (destinoMensaje == _info._identificador)
	{
		switch (mensaje.getEncabezado().getTipoMensaje()) {
			//indican crear flota (es planeta origen)
			case JUGADOR_CREAR_FLOTA:
				ejecutarAvisarCrearFlota(mensaje);
				break;
			//indican crear flota (es planeta destino -debe crearla-)
			case AVISO_CREAR_FLOTA:
				ejecutarCreacionFlota(mensaje);
				break;
			case JUGADOR_CAMBIO_RUMBO_FLOTA:
				ejecutarAvisarCambiarRumboFlota(mensaje);
				break;
			case AVISO_CAMBIO_RUMBO_FLOTA:
				ejecutarCambiarRumboFlota(mensaje);
				break;
			case CAMBIO_DUENO:
				ejecutarCambioDueno(mensaje);
				break;
			case ABANDONA_JUGADOR:
				//se detiene la ejecucion
				this->ejecutarAbandonaJugador(mensaje);
				break;
			case FIN_JUEGO:
				//se detiene la ejecucion
				this->stop();
				break;
			default:
				logger << LogType::LOG_WARNING << LogColor::MAGENTA <<
				"Tipo de mensaje no reconocido: "
						<< mensaje.getEncabezado().getTipoMensaje() << logger.endl;

				break;
		}
	} else {
		logger << LogType::LOG_WARNING << LogColor::MAGENTA <<
				"Mensaje recibido para destino invalido: "
				<< destinoMensaje << logger.endl;
	}

}

} //namespace planeta
} //namespace aplicacion
